
/*
 * The classes in this js file are in charge of all communications with Widget Framework server.
 *
 * Ref:
 *   It based on mootools class Request and has some codes from Request.JSON.
 *   Request: http://mootools.net/docs/core/Request/Request
 *   Request.JSON: http://mootools.net/docs/core/Request/Request.JSON
 */

/*
 *  Class: WFRequest
 *
 *  This is the base HTTP request object for widget framework.
 *  We are going to reuse MooTools' Request object and extend:
 *    1. Timeout control.
 *    2. Support both JSON and XML response object.
 *    3. Detection of 'session expired'.
 *    4. Add CGI parameters automatically.
 *
 *  Options:
 *    1. timeout: number (in mini-second)
 *    2. useJSON: true/false
 *
 *  Events:
 *    1. onTimeout
 *    2. onSessionExpired
 *
 *  Constructor:
 *    var myRequestObj = new WFRequest(options); // options = {option1:value1, option2:value1, ...};
 *
 *  TODO:
 *    - each saving will check server return value, the return value includes last upadted by whom
 *      so we can check do we need to update latest info
 */

WFRequest = new Class({

	Extends: Request,

	options: {
		/*onTimeout:$empty,*/
		/*onSessionExpired:$empty,*/
		useJSON: true, // use json response. If you set to false, you would get a XML object instead of JSON object.
		timeout: ajaxTimeoutValue // defined in includes/product/pageSetting.js
	},

	// Error Flag
	isTimeout: false,
	isSessionExpired: false,
	isFailure: false,
	isBusy: false,

	initialize: function(options) {
		this.parent(options);
		// set JSON header
		$extend(this.headers,{'Accept': 'application/json', 'X-Request': 'JSON'});
	},

	// set timeout
	// - You can use WFRequest.setOptions({timeout:5000}) to set timeout as well.
	setTimeout: function(mseconds) {
		this.setOptions({timeout:mseconds});
	},

	// set server cgi program (path)
	setServerCGI: function(servercgi) {
		wfServerCGI = servercgi+'?'+AddUrlParam(); // AddUrlParam() - global plug-in function for product
		this.setOptions({url:wfServerCGI});
	},

	// extend send()
	// - Add timeout trigger.
	send: function(options) {
		var timeout = (this.options.timeout || (options ? options.timeout: null));
		if (timeout) {
			this.timeoutTimer = window.setTimeout(this.callTimeout.bindWithEvent(this), timeout);
			this.addEvent('onComplete', this.removeTimer);
		}
		return this.parent(options);
	},

	// timeout handler
	// - It would abort the request automatically.
	// - It would trigger onTimeout, onComplete, and onFailure.
	callTimeout: function() {
		// set flag
		this.isTimeout = true;
		this.isFailure = true;

		if (!this.running) return this;
		this.running = false;
		this.xhr.abort();
		this.xhr.onreadystatechange = $empty;
		this.xhr = new Browser.Request();
		this.onFailure();
		this.fireEvent('onTimeout', this);
	},
 
	// When request completed, the timeout will be cleared.
	removeTimer: function() {
		window.clearTimeout(this.timeoutTimer);
	},

	// overwrite success()
	// - reponse type:
	//     useJSON == true  : you will get 'text' and 'json'
	//     useJSON == false : you will get 'text' and 'xml'
	// - detection of 'session expired'
	success: function(text, xml) {
		// check 'session expired'
		if( WFC_isSessionExpired(this.xhr, this.xhr.responseText) ) {
			// set flag
			this.isSessionExpired = true;
			// call handler
			WFC_handler_SessionExpired(this.xhr, this.xhr.responseText);
			this.fireEvent('sessionExpired', this);
			return;
		}
		// check response data type
		if( this.options.useJSON == true ) {
			this.response.json = JSON.decode(text, true); // true means 'secure = true';
			this.onSuccess(text, this.response.json, this);
		} else {
			this.onSuccess(this.processScripts(text), xml, this);
		}
	},

	// overwrite failure()
	// - detection of 'session expired'
	failure: function() {
		// check 'session expired'
		if( WFC_isSessionExpired(this.xhr, this.xhr.responseText) ) {
			WFC_handler_SessionExpired(this.xhr, this.xhr.responseText);
			this.fireEvent('sessionExpired', this);
			// set flag
			this.isSessionExpired = true;
		}
		// set flag
		this.isFailure = true;
		this.onFailure();
	},

	// overwrite onFailure()
	// - pass this instead of this.xhr (we need those error flags.)
	onFailure: function(){
		this.fireEvent('complete', this).fireEvent('failure', this);
	},

	// status checker
	is_timeout: function() {
		return this.isTimeout;
	},
	is_sessionexpired: function() {
		return this.isSessionExpired;
	},
	is_failure: function() {
		return this.isFailure;
	}
});

/*
 *  Class: WFRequest.XML
 *
 *  1.A wrapper for WFRequest. Callback functions would get a XML object.
 *
 */
WFRequest.XML = new Class({
	Extends: WFRequest,
	options: {
		useJSON: false // disable JSON decode
	}
}),

/*
 *  Class: WFRequest.JSON
 *
 *  1.A wrapper for WFRequest. Callback functions would get a JSON object.
 *  2.The class understand widget framework protocol, so we have additional events, and you can have additional callbacks. (which is more convenient)
 *    - onResponseOK
 *    - onResponseERR
 *    - onResponseJSON
 *
 */
WFRequest.JSON = new Class({
	Extends: WFRequest,
	options: {
		/*onResponseOK:$empty,*/
		/*onResponseERR:$empty,*/
		headers: {"Content-type": "application/json"},
		useJSON: true, // enable JSON decode
		urlEncoded: false
	},

	// extend onSuccess()
	onSuccess: function(text, response, reqObj){
		// parent first
		this.parent(arguments);
		if( isset(response) ) {
			// check response, and fire my events
			if(response !== null && isset(response.response) && response.response == "OK" ) {
				this.fireEvent('responseOK', arguments);
			}
			else if(response !== null && isset(response.response) && response.response == "ERR" ) {
				this.fireEvent('responseERR', arguments);
			}
			this.fireEvent('responseJSON', arguments); // fire this event anyway
		}
	}
});

var objProxyPool = {};
WFProxy = new Class({

	Implements: [Chain, Events, Options],

	options: {
		serverCGI: wfc_servercgi_proxyrequest,
		async: true,
		useJSON: false // enable JSON decode
	},

	initialize: function(options) {
		// set options
		if( isset(options) ) {
			this.setOptions(options);
		}
	},

	fetch: function( data, myoptions, cb_functions) {
		var proxyObj,
		    myoptions_encode = JSON.encode(myoptions),
            serverCGI = myoptions && myoptions.serverCGI !== undefined ? myoptions.serverCGI : this.options.serverCGI,
            pool_id = myoptions && myoptions.pool_id !== undefined ? myoptions.pool_id : '',
            key = serverCGI+data+'pool_id'+pool_id;
		if(pool_id && objProxyPool[key] !== undefined && objProxyPool[key]['myoptions'] == myoptions_encode){ //If use objProxyPool && proxyObj exists in pool
			proxyObj = objProxyPool[key];
			 
			// user can change onSuccess & onFailure callback if re-send the request from refreshMod
			if(proxyObj['$events'] !== undefined){
				if(proxyObj['$events']['success'] !== undefined && proxyObj['$events']['success'][0] !== undefined){
					proxyObj['$events']['success'][0] = cb_functions.onSuccess;
				}
				if(proxyObj['$events']['failure'] !== undefined && proxyObj['$events']['failure'][0] !== undefined){
					proxyObj['$events']['failure'][0] = cb_functions.onFailure;
				}
			}
		}else{ //If proxyObj not exists in pool
			// set options (to this object)
			this.setOptions(myoptions);
			// create Request object
			if( this.options.useJSON == true ) {
				var proxyObj = new WFRequest.JSON({headers:'',method:'get',urlEncode:true});
			} else {
				var proxyObj = new WFRequest.XML();
			}
			// set options (to Request object)
			proxyObj.setOptions(this.options);
			// set server cgi
			proxyObj.setServerCGI(this.options.serverCGI);
			// data
			proxyObj.setOptions({data:data});
			// handlers
			proxyObj.addEvents(cb_functions);
			// other options
			//proxyObj.setOptions(myoptions);
		    
			if(pool_id){
				//Save to objProxyPool
				objProxyPool[key] = proxyObj;
				objProxyPool[key]['myoptions'] = myoptions_encode;
			}
		}
		// send
		proxyObj.send();
		// if myoptions.serverCGI is set by user, after sent request, set serverCGI to default value = wfc_servercgi_proxyrequest
        if(myoptions && myoptions.serverCGI !== undefined && myoptions.serverCGI){
             this.options.serverCGI  = wfc_servercgi_proxyrequest;
        }
		return proxyObj;
	}
});

WFData = new Class({

	Implements: [Chain, Events, Options],

	options: {
		/*onTimeout:$empty,*/
		/*onSessionExpired:$empty,*/
		/*onResponseOK:$empty,*/
		/*onResponseERR:$empty,*/
		/*onRequestError:$empty,*/
		serverCGI: wfc_servercgi_wftalk,
		async: true
	},

	// request objects (In order to prevent running in the same time, we use separate objects.)
	requestObj_udata: null,
	requestObj_wdata: null,
	requestObj_save: null,
	requestObj_Wdelete: null,
	requestObj_Wupdate: null,

	// main data objects
	wfUdata: null,
	wfPdata: null,
	wfWdata: null,
	wfTheme: 'default',
	wfLang: 'en_US',

	// flags
	isUDataReady: false,
	isWDataReady: false,

	initialize: function(options) {
		// set options
		if( isset(options) ) {
			this.setOptions(options);
		}

		// run 1st round data sync
		this.fetchUData();
		this.fetchWData();
	},

	fetchUData: function() {
		if( this.requestObj_udata ) {
			if( this.requestObj_udata.running == true ) {
				return false;
			}
			delete this.requestObj_udata;
			this.requestObj_udata = null;
		}
		// create request object
		this.requestObj_udata = new WFRequest.JSON();
		this.requestObj_udata.setOptions(this.options);
		// set server cgi
		this.requestObj_udata.setServerCGI(this.options.serverCGI);
		// set data
		this.requestObj_udata.setOptions({async:false, data:'{"act":"getUdataAll", "cid":"'+wfcid+'"}'});
		// set handlers
		//this.requestObj_udata.setOptions({
		this.requestObj_udata.addEvents({
			onResponseOK:this.request_udata_ok.bind(this),
			onResponseERR:this.request_error.bind(this),
			onFailure:this.request_error.bind(this)
		});
		// send
		//this.requestObj_udata.send().bind(this.requestObj_udata);
		this.requestObj_udata.send();
		return true;
	},

	fetchWData: function() {
		if( this.requestObj_wdata ) {
			if( this.requestObj_wdata.running == true ) {
				return false;
			}
			delete this.requestObj_wdata;
			this.requestObj_wdata = null;
		}
		// create request object
		this.requestObj_wdata = new WFRequest.JSON();
		this.requestObj_wdata.setOptions(this.options);
		// set server cgi
		this.requestObj_wdata.setServerCGI(this.options.serverCGI);
		// set data
		this.requestObj_wdata.setOptions({async:false, data:'{"act":"getWdataAll", "cid":"'+wfcid+'"}'});
		// set handlers
		//this.requestObj_wdata.setOptions({
		this.requestObj_wdata.addEvents({
			onResponseOK:this.request_wdata_ok.bind(this),
			onResponseERR:this.request_error.bind(this),
			onFailure:this.request_error.bind(this)
		});
		// send
		//this.requestObj_wdata.send().bind(requestObj_wdata);
		this.requestObj_wdata.send();
		return true;
	},

	/*
	 * event handlers
	 */
	request_udata_ok: function(text, response) {
		this.wfUdata = response.result.udata;
		this.wfPdata = response.result.pdata;
		this.wfTheme = response.result.theme;
		this.wfLang = response.result.lang;
		this.isUDataReady = true;
	},

	request_wdata_ok: function(text, response) {
		this.wfWdata = response.result;
		this.isWDataReady = true;
	},

	request_error: function(reqObj) {
		// check codition, fire events
	},

	/*
	 * methods for others
	 */
	// isDataReady
	isDataReady: function() {
		if( this.isUDataReady == true && this.isWDataReady == true ) {
			return true;
		}
		return false;
	},
	// udata
	getMyUdata: function(wid) {
		if( this.wfUdata == null ) {
			return null;
		}
		for( var id = 0; id < this.wfUdata.length; id++) {
			if( this.wfUdata[id].wid == wid ) {
				return this.wfUdata[id];
			}
		}
		return null;
	},
	getMyUdata_field: function(wid, fname) {
		if( this.wfUdata == null ) {
			return null;
		}
		for( var id = 0; id < this.wfUdata.length; id++) {
			if( this.wfUdata[id].wid == wid ) {
				return this.wfUdata[id][fname];
			}
		}
		return null;
	},
	// pdata
	getMyPdata: function(pid) {
		if( this.wfPdata == null ) {
			return null;
		}
		for( var id = 0; id < this.wfPdata.length; id++) {
			if( this.wfPdata[id].pageNumber == pid ) {
				return this.wfPdata[id];
			}
		}
		return null;
	},
	getMyPdata_field: function(pid, fname) {
		if( this.wfPdata == null ) {
			return null;
		}
		for( var id = 0; id < this.wfPdata.length; id++) {
			if( this.wfPdata[id].pageNumber == pid ) {
				return this.wfPdata[id][fname];
			}
		}
		return null;
	},
	// theme
	getMyTheme: function() {
		return this.wfTheme;
	},
	// lang
	getMyLang: function() {
		return this.wfLang;
	},
	// wdata
	getMyWdata: function(wid) {
		if( this.wfWdata == null ) {
			return null;
		}
		for( var id = 0; id < this.wfWdata.length; id++) {
			if( this.wfWdata[id].wid == wid ) {
				return this.wfWdata[id];
			}
		}
		return null;
	},
	getMyWdataData: function(wid) {
		if( this.wfWdata == null ) {
			return null;
		}
		for( var id = 0; id < this.wfWdata.length; id++) {
			if( this.wfWdata[id].wid == wid ) {                
				return this.wfWdata[id].data;
			}
		}
		return null;
	},
	getMyWMiscData: function(wid) {
		if( this.wfWdata == null ) {
			return null;
		}
		for( var id = 0; id < this.wfWdata.length; id++) {
			if( this.wfWdata[id].wid == wid && this.wfWdata[id].miscData) {
				return this.wfWdata[id].miscData;
			}
		}
		return null;
	},
	getMyWdataParam: function(wid) {
		if( this.wfWdata == null ) {
			return null;
		}
		for( var id = 0; id < this.wfWdata.length; id++) {
			if( this.wfWdata[id].wid == wid ) {
				return this.wfWdata[id].param;
			}
		}
		return null;
	},
	getMyWdataKey: function(wid) {
		if( this.wfWdata == null ) {
			return null;
		}
		for( var id = 0; id < this.wfWdata.length; id++) {
			if( this.wfWdata[id].wid == wid ) {
				return this.wfWdata[id].widget_key;
			}
		}
		return null;
	},
	getMyWdataPid: function(wid) {
		if( this.wfWdata == null ) {
			return null;
		}
		for( var id = 0; id < this.wfWdata.length; id++) {
			if( this.wfWdata[id].wid == wid ) {
				return this.wfWdata[id].pid;
			}
		}
		return null;
	},

	// delete widget (from userdata)
	WdataDelete: function(wid, cb_functions) {
		if( this.wfWdata == null ) {
			return;
		}
		if( this.requestObj_Wdelete ) {
			if( this.requestObj_Wdelete.running == true ) {
				this.requestObj_Wdelete.isBusy = true;
				/*
				if( isset(cb_functions.onFailure) ) {
					cb_functions.onFailure(this.requestObj_Wdelete);
				}
				*/
				return;
			}
			delete this.requestObj_Wdelete;
			this.requestObj_Wdelete = null;
		}
		// create request object
		this.requestObj_Wdelete = new WFRequest.JSON();
		this.requestObj_Wdelete.setOptions(this.options);
		this.Wdelete_sync_wid = wid;
		// set server cgi
		this.requestObj_Wdelete.setServerCGI(this.options.serverCGI);
		// set data
		this.requestObj_Wdelete.setOptions({async:true, data:JSON.encode({act:'moduledelete', pid:wfc_mainScreen.currActive, wid:wid})});
		// set handlers
		this.requestObj_Wdelete.addEvents(cb_functions);
		// set my handlers
		this.requestObj_Wdelete.addEvents({onResponseOK:this.WdataDelete_sync.bind(this)});
		// send
		this.requestObj_Wdelete.send();
	},
	WdataDelete_sync: function() {
		if( this.wfWdata == null ) {
			return;
		}
		if( isset(this.wfWdata[this.Wdelete_sync_wid]) ) {
			this.wfWdata.erase(this.Wdelete_sync_wid);
		}
	},

	// update widget data (from userdata)
	WdataUpdate: function(wid, data, cb_functions) {
		if( this.wfWdata == null ) {
			return true;
		}
		if( this.requestObj_Wupdate ) {
			if( this.requestObj_Wupdate.running == true ) {
				this.requestObj_Wupdate.isBusy = true;
				if( isset(cb_functions) && isset(cb_functions.onFailure) ) {
					cb_functions.onFailure(this);
				}
			}
			delete this.requestObj_Wupdate;
			this.requestObj_Wupdate = null;
		}
		// create request object
		this.requestObj_Wupdate = new WFRequest.JSON();
		this.requestObj_Wupdate.setOptions(this.options);
		this.Wupdate_sync_wid = wid;
		this.Wupdate_sync_data = data;
		// set server cgi
		this.requestObj_Wupdate.setServerCGI(this.options.serverCGI);
		// set data
		this.requestObj_Wupdate.setOptions({async:true, data:JSON.encode({act:'module', pid:wfc_mainScreen.currActive, wid:wid, data:data})});
		// set handlers
		this.requestObj_Wupdate.addEvents(cb_functions);
		// set my handlers
		this.requestObj_Wupdate.addEvents({onResponseOK:this.WdataUpdate_sync.bind(this)});
		// send
		this.requestObj_Wupdate.send();
	},
	WdataUpdate_sync: function() {
		if( this.wfWdata == null ) {
			return;
		}
		for( var id = 0; id < this.wfWdata.length; id++) {
			if( this.wfWdata[id].wid == this.Wupdate_sync_wid ) {
                this.wfWdata[id].data = JSON.encode(JSON.decode(this.Wupdate_sync_data)['wfUserData']);
			}
		}
	}
});

